package domain;

import java.util.*;

import entities.role.Role;

public class Vertex {

	private final Object f;
	//private HashSet<Edge> visitedInEdges = new HashSet<Edge>();
	private FeatureGraph fg;
	private HashSet<Edge> inedges;
	private HashSet<Edge> outedges;
	//for every vertex, there is an equivalence set, e.g., for vertex f, f=3, f=g, f=k, then {3, g, k}
	private HashSet<Vertex> equivalenceSet;
	
	public Vertex(Object f, FeatureGraph fg) {
		this.f = f;
		this.fg = fg;
		this.inedges = new HashSet<Edge>();
		this.outedges = new HashSet<Edge>();
		this.equivalenceSet = new HashSet<Vertex>();
		fg.getFVMAP().put(f, this);
	}
	
	public FeatureGraph getAssociatedFeatureGraph(){
		return this.fg;
	}
	
	public Object getDomainObject(){
		return this.f;
	}
	
	public HashSet<Edge> getOutEdges(){
		return this.outedges;
	}
	
	public HashSet<Edge> getInEdges(){
		return this.inedges;
	}

	@Override
	public String toString(){
		String ret = "";
		if(this.f instanceof Role){
			Role rr = (Role)f;
			ret += rr.getRoleName();
		}else{
			ret += f.toString();
		}
		return ret;
	}

	
	/*
	 * f<g, g<=k, k<f is a clash
	 * but f=g, g<=f is not!
	 * DFS search for clashes. 
	 * Note that path only refers to a path with both = and <=.
	 * hasLTSign default: false
	 */
	public boolean computeLTClash(Vertex v){
		return this.computeLTClash(v, new HashSet<Edge>(),false, false);
	}
	
	private boolean computeLTClash(Vertex v, HashSet<Edge> visited, boolean loop, boolean hasLTSign){
		//boolean possible = false;
		for(Edge e:v.outedges){
			Vertex dest = e.dest;
			boolean localLT  = hasLTSign;
			//localLT |= e.isLTEdge();
			localLT |= e.isLTEdge();
			if(visited.contains(e)){
				continue;
			}
			visited.add(e);
			//if found this object and have seen < at least once
			if(dest.equals(this) && localLT){
				loop |= true;
				return loop;
			}else{
				loop |= computeLTClash(dest, visited, loop, localLT);
			}
			 
		}	
		return loop;
	}
	
	/*private boolean computeLTClash(Vertex v, HashSet<Vertex> visited, boolean loop, boolean hasLTSign){
		//boolean possible = false;
		for(Edge e:v.outedges){
			Vertex dest = e.dest;
			boolean localLT  = hasLTSign;
			//localLT |= e.isLTEdge();
			localLT |= e.isLTEdge();
			if(visited.contains(dest)){
				continue;
			}
			visited.add(dest);
			//if found this object and have seen < at least once
			if(dest.equals(this) && localLT){
				loop |= true;
				return loop;
			}else{
				loop |= computeLTClash(dest, visited, loop, localLT);
			}
			 
		}	
		return loop;
	}*/
	
	public boolean computeEQClash(Vertex v){
		//first compute to get all eqs
		this.computeEQClash(v, new HashSet<Vertex>(), false, true, new Stack<Vertex>());
		//saturations
		boolean changed = true;
		while(changed){
			HashSet<Vertex> more = new HashSet<Vertex>();
			for(Vertex vv:v.equivalenceSet){
				if(vv.equivalenceSet.size()>0){
					more.addAll(vv.equivalenceSet);
				}
			}
			changed &= !v.equivalenceSet.containsAll(more);
			if(changed){
				v.equivalenceSet.addAll(more);
			}
		}
		//test if >=2 concrete values occur in this set
		HashSet<String> s = new HashSet<String>();
		for(Vertex vc:v.equivalenceSet){
			if(vc.f instanceof String){
				String ss = (String) vc.f;
				ss = ss.trim();
				s.add(ss);
			}
		}
		if(s.size()>1){
			return true;
		}		
		return false;
	}
	
	/*
	 * f<=g, g=h and a loop form no clashes, but a set of eq concepts.
	 * loop defaults to false; allEqs defaults to true;
	 */
	private boolean computeEQClash(Vertex v, HashSet<Vertex> visited, boolean loop, boolean allEqs, Stack<Vertex> path){
		//System.out.println("For Vertex:"+v.renderDomainObject()+", Objective: "+this.renderDomainObject());
		//Stack<Vertex> localpath = new Stack<Vertex>();
		//localpath.addAll(path);
		//boolean anyloop = false;
		for(Edge e:v.outedges){
			//System.out.println("Now testing:"+e.toString());
			if(e.isLTEdge()){
				//only EQ LTEQ edges considered
				continue;
			}
			boolean localEQ = allEqs;
			localEQ &= e.isEqEdge();
			//System.out.println("All EQs:"+allEqs);
			Vertex dest = e.dest;
			if(visited.contains(dest)){
				continue;
			}
			visited.add(dest);
			if(localEQ){
				//equivalences along this path
				this.equivalenceSet.add(dest);
			}
			if(dest.equals(this)){
				loop |= true;
				path.push(dest);
			}else{
				loop |= computeEQClash(dest, visited, loop, localEQ, path);
			}				
			
			if(loop){
				//add all nodes to eq set
				//path.push(dest);
				//this.equivalenceSet.addAll(path);			
				//System.out.println("Loop found, path:"+this.renderStack(path));
			}
		}
		//System.out.print(renderStack(path));
		this.equivalenceSet.addAll(path);		
		return loop;
	}
	
	public String renderStack(Stack<Vertex> s){
		String st = "";
		int i=s.size()-1;
		while(i>=0){
			Vertex o = s.get(i);
			i--;
			st += o.toString() + "; ";
		}
		return st;
	}

	public void addInEdge(Edge e){
		this.inedges.add(e);
	}
	
	public void addOutEdge(Edge e){
		this.outedges.add(e);
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((f == null) ? 0 : f.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (! (obj instanceof Vertex))
			return false;
		if (getClass() != obj.getClass())
			return false;
		Vertex other = (Vertex) obj;
		if (f == null) {
			if (other.f != null)
				return false;
		} else if (!f.equals(other.f))
			return false;
		return true;
	}

	

	
}
